package edu.cicese.alz;

import com.jgoodies.looks.windows.WindowsLookAndFeel;
import com.toedter.calendar.JCalendar;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
import org.openstreetmap.gui.jmapviewer.MapLabel;
import org.openstreetmap.gui.jmapviewer.MapLine;
import org.openstreetmap.gui.jmapviewer.MapLocation;
import org.openstreetmap.gui.jmapviewer.MapRoute;
import org.openstreetmap.gui.jmapviewer.OsmFileCacheTileLoader;
import org.openstreetmap.gui.jmapviewer.OsmTileLoader;
import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource;
import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
import org.openstreetmap.travelingsalesman.routing.Route;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by: Eduardo Quintana Contreras
 * Date: 12/01/12
 * Time: 11:16 AM
 */
public class Map extends JFrame {

	private Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
	private JMapViewer map;
	private List<MapMarker> mapMarkers;
	private List<MapLine> mapLines;
	private List<MapLabel> mapLabels;
	private List<MapLocation> mapLocations;
	private List<MapRoute> mapRoutes = new ArrayList<>();
	private List<URoute> uRoutes;
	private boolean mapMarkersVisible = true;
    private boolean mapLinesVisible = true;
    private boolean mapLabelsVisible = false;
    private boolean mapLocationsVisible = true;
	private boolean mapRoutesVisible = false;
//	private int markersShown;
	private JSlider jsMarkers;
	private int userID;
	private String date;
	private SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
	private SimpleDateFormat dfFolder = new SimpleDateFormat("yyyy-MM-dd");
	private JLabel lblDate;
	private JCalendar calendar;
	private static boolean userChanged = false;

	public Map(int userID, String date, List<MapMarker> mapMarkers, List<MapLine> mapLines, List<MapLabel> mapLabels,
	           List<MapLocation> mapLocations) {
		this(userID, date, mapMarkers, mapLines, mapLabels, mapLocations, new ArrayList<URoute>(),
		true, true, false, true, false);
	}
	
	public Map(int userID, String date, List<MapMarker> mapMarkers, List<MapLine> mapLines, List<MapLabel> mapLabels,
	           List<MapLocation> mapLocations, List<URoute> uRoutes) {
		this(userID, date, mapMarkers, mapLines, mapLabels, mapLocations, uRoutes,
		true, true, false, true, true);
	}

	public Map(int userID, String date, List<MapMarker> mapMarkers, List<MapLine> mapLines, List<MapLabel> mapLabels,
	           List<MapLocation> mapLocations, List<URoute> uRoutes, boolean mapMarkersVisible, boolean mapLinesVisible,
	           boolean mapLabelsVisible, boolean mapLocationsVisible, boolean mapRoutesVisible) {
		System.setProperty("http.proxyHost", "158.97.7.197");
		System.setProperty("http.proxyPort", "3128");
		this.mapMarkers = mapMarkers;
		this.mapLines = mapLines;
		this.mapLabels = mapLabels;
		this.mapLocations = mapLocations;
		this.uRoutes = uRoutes;
		this.userID = userID;
		this.date = date;
		this.mapMarkersVisible = mapMarkersVisible;
		this.mapLinesVisible = mapLinesVisible;
		this.mapLabelsVisible = mapLabelsVisible;
		this.mapLocationsVisible = mapLocationsVisible;
		this.mapRoutesVisible = mapRoutesVisible;

		try {
			UIManager.setLookAndFeel(new WindowsLookAndFeel());
			UIManager.put("Panel.background", Color.WHITE);
			UIManager.put("OptionPane.background", Color.WHITE);
			UIManager.put("CheckBox.background", Color.WHITE);
			UIManager.put("CheckBox.focus", Color.WHITE);
			UIManager.put("ComboBox.background", Color.WHITE);
			UIManager.put("ComboBox.focus", Color.WHITE);
			UIManager.put("Button.focus", new Color(0, 0, 0, 0));
			UIManager.put("Slider.focus", new Color(0, 0, 0, 0));
//			UIManager.put("TextArea.inactiveBackground", Color.WHITE);
//			UIManager.put("ProgressBar.selectionBackground", Color.DARK_GRAY);
//			UIManager.put("ProgressBar.selectionForeground", Color.WHITE);
			UIManager.put("ToolTip.background", new Color(229, 244, 255));
			UIManager.put("ToolTip.foreground", Color.DARK_GRAY);
			LineBorder toolTipBorder = new LineBorder(new Color(173, 210, 255), 1);
			UIManager.put("ToolTip.border", toolTipBorder);
			Font tooltipFont = new Font("Segoe UI", Font.BOLD, 12);
			UIManager.put("ToolTip.font", tooltipFont);
		} catch (UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		}

		setTitle("Map viewer");
		setSize(1250, 900);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Rectangle window = getBounds();
		setLocation((screen.width - window.width) / 2, (screen.height - window.height) / 2);
		setExtendedState(JFrame.MAXIMIZED_BOTH);

		initComponents();
	}

	private void initComponents() {
		List<String> auxRoutes = new ArrayList<>();
		auxRoutes.add("All");
		int i = 1;
		for (URoute uRoute : uRoutes) {
//			System.out.println("From " + uRoute.getStartLocationID() + " to " + uRoute.getEndLocationID());
			if (uRoute.getRoute() != null) {
				auxRoutes.add(uRoute.getStartLocationID() + " -> " + uRoute.getEndLocationID());
				System.out.println("Route " + i + " added.");
				for (Route.RoutingStep rs : uRoute.getRoute().getRoutingSteps()) {
					mapRoutes.add(new MapRoute(rs.getStartNode().getLatitude(), rs.getStartNode().getLongitude(),
							rs.getEndNode().getLatitude(), rs.getEndNode().getLongitude(), i,
							uRoute.getStartLocationID(), uRoute.getEndLocationID()));
				}
				i++;
			}
		}

		String[] sRoutes = new String[i];
//		System.out.println(auxRoutes.size() + " routes. " + i);
		for (int x = 0; x < i; x++) {
			sRoutes[x] = auxRoutes.get(x);
		}

		map = new JMapViewer(mapMarkers, mapLines, mapLabels, mapLocations, mapRoutes,
				mapMarkersVisible, mapLinesVisible, mapLabelsVisible, mapLocationsVisible, mapRoutesVisible);

		JPanel pMain = new JPanel();
		pMain.setLayout(new BorderLayout());

		// Button panel
		JPanel pButtons = new JPanel();
		pButtons.setLayout(null);
		pButtons.setPreferredSize(new Dimension(165, 500));

		// Fit markers
		JButton btnFitMarkers = new JButton("Fit markers");
        btnFitMarkers.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setDisplayToFitMapMarkers();
	        }
        });
		btnFitMarkers.setBounds(10, 10, 100, 30);
		pButtons.add(btnFitMarkers);

		// Tile source
        final JComboBox cbTileSourceSelector = new JComboBox(new TileSource[] { new OsmTileSource.Mapnik(),
		        new OsmTileSource.TilesAtHome(), new OsmTileSource.CycleMap(), new BingAerialTileSource() });
        cbTileSourceSelector.addItemListener(new ItemListener() {
	        public void itemStateChanged(ItemEvent e) {
		        map.setTileSource((TileSource) e.getItem());
	        }
        });
		cbTileSourceSelector.setBounds(10, 50, 130, 30);
		pButtons.add(cbTileSourceSelector);

		// Tile loader
        JComboBox cbTileLoaderSelector;
        try {
            cbTileLoaderSelector = new JComboBox(new TileLoader[] { new OsmFileCacheTileLoader(map),
                    new OsmTileLoader(map) });
        } catch (IOException e) {
            cbTileLoaderSelector = new JComboBox(new TileLoader[] { new OsmTileLoader(map) });
        }
        cbTileLoaderSelector.addItemListener(new ItemListener() {
	        public void itemStateChanged(ItemEvent e) {
		        map.setTileLoader((TileLoader) e.getItem());
	        }
        });
        map.setTileLoader((TileLoader) cbTileLoaderSelector.getSelectedItem());
		cbTileLoaderSelector.setBounds(10, 90, 130, 30);
		pButtons.add(cbTileLoaderSelector);

		// Show/hide markers
        final JCheckBox chbShowMarkers = new JCheckBox("Show markers");
        chbShowMarkers.setSelected(map.getMapMarkersVisible());
        chbShowMarkers.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setMapMarkerVisible(chbShowMarkers.isSelected());
	        }
        });
		chbShowMarkers.setBounds(10, 130, 100, 30);
		pButtons.add(chbShowMarkers);

		// Show/hide tracks
	    final JCheckBox chbShowTracks = new JCheckBox("Show tracks");
        chbShowTracks.setSelected(map.getMapLinesVisible());
        chbShowTracks.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setMapLineVisible(chbShowTracks.isSelected());
	        }
        });
		chbShowTracks.setBounds(10, 170, 100, 30);
		pButtons.add(chbShowTracks);

		// Show/hide labels
	    final JCheckBox chbShowLabels = new JCheckBox("Show labels");
        chbShowLabels.setSelected(map.getMapLabelsVisible());
        chbShowLabels.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setMapLabelVisible(chbShowLabels.isSelected());
	        }
        });
		chbShowLabels.setBounds(10, 210, 100, 30);
		pButtons.add(chbShowLabels);

		// Show/hide locations
	    final JCheckBox chbShowLocations = new JCheckBox("Show locations");
        chbShowLocations.setSelected(map.getMapLocationsVisible());
        chbShowLocations.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setMapLocationVisible(chbShowLocations.isSelected());
	        }
        });
		chbShowLocations.setBounds(10, 250, 100, 30);
		pButtons.add(chbShowLocations);

		// Show/hide grid
        final JCheckBox chbShowGrid = new JCheckBox("Show grid");
        chbShowGrid.setSelected(map.isTileGridVisible());
        chbShowGrid.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setTileGridVisible(chbShowGrid.isSelected());
	        }
        });
		chbShowGrid.setBounds(10, 290, 100, 30);
		pButtons.add(chbShowGrid);

		// Show/hide zoom controls
        final JCheckBox chbShowZoomControls = new JCheckBox("Show zoom controls");
        chbShowZoomControls.setSelected(map.getZoomContolsVisible());
        chbShowZoomControls.addActionListener(new ActionListener() {

	        public void actionPerformed(ActionEvent e) {
		        map.setZoomContolsVisible(chbShowZoomControls.isSelected());
	        }
        });
		chbShowZoomControls.setBounds(10, 330, 130, 30);
		pButtons.add(chbShowZoomControls);

		final JComboBox cbUser = new JComboBox(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
		cbUser.setSelectedItem(userID);
		cbUser.setBounds(10, 370, 50, 30);
		cbUser.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (!userChanged) {
					userID = cbUser.getSelectedIndex() + 1;
					userChanged = true;
					reload();
				}
				else {
					userChanged = false;
				}
			}
		});
		pButtons.add(cbUser);

		lblDate = new JLabel();
		lblDate.setBounds(5, 550, 100, 30);
		lblDate.setText(date);
		pButtons.add(lblDate);

		calendar = new JCalendar();
		calendar.setBounds(5, 410, 150, 145);
		calendar.setWeekOfYearVisible(false);
		Date d = new Date();
		try {
			d = df.parse(date);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		calendar.setDate(d);
		calendar.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent e) {
//				System.out.println(e.getPropertyName());
				if ("calendar".equals(e.getPropertyName())) {
					if (!userChanged) {
						reload();
					}
					else {
						userChanged = false;
					}
				}
			}
		});
		pButtons.add(calendar);


		// Routes

		JLabel lblRoutes = new JLabel();
		lblRoutes.setBounds(5, 600, 100, 30);
		lblRoutes.setText("Rutas");
		lblRoutes.setFont(new Font("Sans Serif", Font.BOLD, 25));
		pButtons.add(lblRoutes);

		// Show/hide routes
	    final JCheckBox chbShowRoutes = new JCheckBox("Show routes");
        chbShowRoutes.setSelected(map.getMapRoutesVisible());
        chbShowRoutes.addActionListener(new ActionListener() {
	        public void actionPerformed(ActionEvent e) {
		        map.setMapRouteVisible(chbShowRoutes.isSelected());
	        }
        });
		chbShowRoutes.setBounds(10, 640, 100, 30);
		pButtons.add(chbShowRoutes);

		final JComboBox cbRoutes = new JComboBox(sRoutes);
//		cbRoutes.setSelectedItem(userID);
		cbRoutes.setBounds(10, 690, 100, 30);
		cbRoutes.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				map.setRoutes(cbRoutes.getSelectedIndex());
			}
		});
		pButtons.add(cbRoutes);


		setTitle("User: " + userID + " - " + date);
        pMain.add(map, BorderLayout.CENTER);
		pMain.add(pButtons, BorderLayout.WEST);
		getContentPane().add(pMain);


		Action actionSave = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
	            Date d;
	            String dateFolder = "";
	            try {
		            d = df.parse(date);
		            dateFolder = dfFolder.format(d);
	            } catch (ParseException e1) {
		            e1.printStackTrace();
	            }
//	            saveMap("D:\\_proyectoAlz\\analysis\\users\\incense" + userID + "\\" + dateFolder + "\\" +
//		                System.currentTimeMillis());
	            saveMap("D:\\_proyectoAlz\\analysis\\users\\" + userID + System.currentTimeMillis());
            }
        };
		Action actionFit = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				 map.setDisplayToFitMapMarkers();
			}
		};
		Action actionAddPoint = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
	            map.addPoint();
            }
        };
		Action actionRemovePoint = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
	            map.removePoint();
            }
        };
		Action actionBeginning = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
	            map.setPoints(0);
            }
        };
		Action actionEnding = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
	            map.setPoints(map.jsMarkers.getMaximum());
            }
        };
		Action actionOne = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				 map.setTileSource((TileSource) cbTileSourceSelector.getItemAt(0));
			}
		};
		Action actionTwo = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				 map.setTileSource((TileSource) cbTileSourceSelector.getItemAt(1));
			}
		};
		Action actionThree = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				 map.setTileSource((TileSource) cbTileSourceSelector.getItemAt(2));
			}
		};
		Action actionFour = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				 map.setTileSource((TileSource) cbTileSourceSelector.getItemAt(3));
			}
		};

		InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
		ActionMap actionMap = getRootPane().getActionMap();

		inputMap.put(KeyStroke.getKeyStroke("control S"), "save");
		inputMap.put(KeyStroke.getKeyStroke("F"), "fit");
		inputMap.put(KeyStroke.getKeyStroke("RIGHT"), "add");
		inputMap.put(KeyStroke.getKeyStroke("LEFT"), "remove");
		inputMap.put(KeyStroke.getKeyStroke("HOME"), "beginning");
		inputMap.put(KeyStroke.getKeyStroke("END"), "ending");
		inputMap.put(KeyStroke.getKeyStroke("NUMPAD1"), "one");
		inputMap.put(KeyStroke.getKeyStroke("NUMPAD2"), "two");
		inputMap.put(KeyStroke.getKeyStroke("NUMPAD3"), "three");
		inputMap.put(KeyStroke.getKeyStroke("NUMPAD4"), "four");

		actionMap.put("save", actionSave);
		actionMap.put("fit", actionFit);
		actionMap.put("add", actionAddPoint);
		actionMap.put("remove", actionRemovePoint);
		actionMap.put("beginning", actionBeginning);
		actionMap.put("ending", actionEnding);
		actionMap.put("one", actionOne);
		actionMap.put("two", actionTwo);
		actionMap.put("three", actionThree);
        actionMap.put("four", actionFour);


		setVisible(true);
		map.setDisplayToFitMapMarkers();
	}

	private void reload() {
		setVisible(false);
		dispose();
		new SQLQuery().displayGPSData(userID, df.format(calendar.getCalendar().getTime()), false, false);
	}

	protected void saveMap(String filename) {
		BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2 = bi.createGraphics();
		map.paint(g2);
		g2.dispose();
		try{
			ImageIO.write(bi, "png", new File(filename + ".png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*private void addPoint() {
		if (markersShown < mapMarkers.size()) {
			map.addMapMarker(mapMarkers.get(markersShown));
			map.addMapLabel(mapLabels.get(markersShown));
			markersShown++;
			if (markersShown > 1) {
				map.addMapLine(mapLines.get(markersShown - 2));
			}
		}
	}

	private void removePoint() {
		if (markersShown > 0) {
			map.removeMapMarker(mapMarkers.get(markersShown - 1));
			map.removeMapLabel(mapLabels.get(markersShown - 1));
			markersShown--;
			if (markersShown != 0) {
				map.removeMapLine(mapLines.get(markersShown - 1));
			}
		}
	}*/

	public static void main(String[] args) {
		/*System.setProperty("http.proxyHost", "158.97.7.197");
		System.setProperty("http.proxyPort", "3128");

		List<MapMarker> markers = new ArrayList<>();
		markers.add(new MapMarkerDot(49.814284999, 8.642065999));
		markers.add(new MapMarkerDot(49.814284999, 6.642065999));
		markers.add(new MapMarkerDot(49.814284999, 5.642065999));
		markers.add(new MapMarkerDot(49.814284999, 4.642065999));

		List<MapLabel> labels = new ArrayList<>();
		labels.add(new MapLabel("Marker 1", 49.814284999, 8.642065999));
		labels.add(new MapLabel("Marker 2", 49.814284999, 6.642065999));
		labels.add(new MapLabel("Marker 3", 49.814284999, 5.642065999));
		labels.add(new MapLabel("Marker 4", 49.814284999, 4.642065999));

		List<MapLine> lines = new ArrayList<>();
		lines.add(new MapLine(49.814284999, 8.642065999, 49.814284999, 6.642065999));
		lines.add(new MapLine(49.814284999, 6.642065999, 49.814284999, 5.642065999));
		lines.add(new MapLine(49.814284999, 5.642065999, 49.814284999, 4.642065999));
		
		new Map(markers, lines, labels, null);*/
	}
}
